#include "symbols.h"
#include <stdio.h>
#include <elf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <zlib.h>

#pragma pack(1)
typedef struct {
	uint64_t address;
	int size;
	int type;
	unsigned char namelength;
	char name[0];
} symbolinfo, *psymbolinfo;
#pragma pack()

#define TEMPBUFSIZE 64*1024
//
//
//void loadStringTable64(int f, Elf64_Shdr *sectionHeaders, unsigned char **stringTable, int index)
//{
//  if ((stringTable[index]==NULL) && (sectionHeaders[index].sh_type==SHT_STRTAB))
//  {
//    stringTable[index]=malloc(sectionHeaders[index].sh_size);
//    if (pread(f, stringTable[index], sectionHeaders[index].sh_size, sectionHeaders[index].sh_offset)==-1)
//    {
//      debug_log("Failure loading the stringtable\n");
//      free(stringTable[index]);
//      stringTable[index]=NULL;
//    }
//
//  }
//  else
//    debug_log("Not a string table\n");
//}
//
//void loadStringTable32(int f, Elf32_Shdr *sectionHeaders, unsigned char **stringTable, int index)
//{
//  if ((stringTable[index]==NULL) && (sectionHeaders[index].sh_type==SHT_STRTAB))
//  {
//    stringTable[index]=malloc(sectionHeaders[index].sh_size);
//    if (pread(f, stringTable[index], sectionHeaders[index].sh_size, sectionHeaders[index].sh_offset)==-1)
//    {
//      debug_log("Failure loading the stringtable\n");
//      free(stringTable[index]);
//      stringTable[index]=NULL;
//    }
//
//  }
//  else
//    debug_log("Not a string table\n");
//}
//
//int ELF32(int f, Elf32_Ehdr *b, unsigned char **output)
///*
//Caller must free output manually
//*/
//{
//  int i,j;
//
//  unsigned char *tempbuffer=NULL;
//  int tempbufferpos=0;
//  int maxoutputsize=TEMPBUFSIZE;
//  tempbuffer=malloc(TEMPBUFSIZE);
//
//  //setup zlib
//  z_stream strm;
//  strm.zalloc = Z_NULL;
//  strm.zfree = Z_NULL;
//  strm.opaque = Z_NULL;
//  deflateInit(&strm, 9);
//
//  *output=malloc(maxoutputsize); //allocate 256KB. This "should" be enough, but reallocate if more is needed
//
//  strm.avail_out=maxoutputsize-3*sizeof(uint32_t); //first if it's an exe, followed by the compressed size, followed by the decompressed size
//  strm.next_out=(unsigned char *)&(*output)[sizeof(uint32_t)*3];
//
//  *(uint32_t *)(&(*output)[0])=(b->e_type==ET_EXEC);
//
///*
//
//  debug_log("e_shoff=%x\n", b->e_shoff);
//  debug_log("e_shentsize=%d\n", b->e_shentsize);
//  debug_log("e_shnum=%d\n", b->e_shnum);
//  debug_log("e_shstrndx=%d\n", b->e_shstrndx);*/
//
//  Elf32_Shdr *sectionHeaders=malloc(b->e_shentsize*b->e_shnum);
//
//  if (pread(f, sectionHeaders, b->e_shentsize*b->e_shnum, b->e_shoff)==-1)
//  {
//    //printf("Failure to read sectionHeaders\n");
//    deflateEnd(&strm);
//    if (sectionHeaders)
//      free(sectionHeaders);
//
//    if (output)
//      free(output);
//
//    if (tempbuffer)
//      free(tempbuffer);
//
//    return -1;
//  }
//
//  unsigned char **stringTable=calloc(b->e_shnum, sizeof(unsigned char*) );
//
//  loadStringTable32(f, sectionHeaders, stringTable, b->e_shstrndx);
//
//
//  for (i=0; i<b->e_shnum; i++)
//  {
//    //printf("Section %d (%x): name=%s\n", i, sectionHeaders[i].sh_addr, &stringTable[b->e_shstrndx][sectionHeaders[i].sh_name]);
//
//    if ((sectionHeaders[i].sh_type==SHT_SYMTAB) || (sectionHeaders[i].sh_type==SHT_DYNSYM))
//    {
//     // debug_log("Symbol data:\n", i);
//
//     // debug_log("sh_addr=%x\n", sectionHeaders[i].sh_addr);
//      //printf("sh_offset=%x\n", sectionHeaders[i].sh_offset);
//      //printf("sh_size=%x\n", sectionHeaders[i].sh_size);
//      //printf("sh_link=%d (string table)\n", sectionHeaders[i].sh_link);
//      //printf("sh_info=%d\n", sectionHeaders[i].sh_info);
//
//      Elf32_Sym *symbolTable=malloc(sectionHeaders[i].sh_size);
//      if (pread(f, symbolTable, sectionHeaders[i].sh_size, sectionHeaders[i].sh_offset)==-1)
//      {
//       // debug_log("Failure reading symbol table\n");
//        return -1;
//      }
//      int maxindex=sectionHeaders[i].sh_size / sizeof(Elf32_Sym);
//
//      loadStringTable32(f, sectionHeaders, stringTable, sectionHeaders[i].sh_link);
//
//      //printf("maxindex=%d\n", maxindex);
//      for (j=0; j<maxindex; j++)
//      {
//        //printf("symbolTable[%d]:\n", i);
//        //printf("st_name=%s\n", &stringTable[sectionHeaders[i].sh_link][symbolTable[j].st_name] );
//       // debug_log("st_value=%x\n", symbolTable[j].st_value);
//        //printf("st_size=%d\n", symbolTable[j].st_size);
//       // debug_log("st_info=%d\n", symbolTable[j].st_info);
//        //printf("  Bind=%d\n", ELF32_ST_BIND(symbolTable[j].st_info));
//        //printf("  Type=%d\n", ELF32_ST_TYPE(symbolTable[j].st_info));
//       // debug_log("st_other=%d\n", symbolTable[j].st_other);
//       // debug_log("st_shndx=%d\n", symbolTable[j].st_shndx);
//
//        if (symbolTable[j].st_value)
//        {
//          //add it to the tempbuffer
//          char *symbolname=(char *)&stringTable[sectionHeaders[i].sh_link][symbolTable[j].st_name];
//          size_t namelength=strlen(symbolname);
//          int entrysize=sizeof(symbolinfo)+namelength;
//          if (tempbufferpos+entrysize>=TEMPBUFSIZE)
//          {
//             //compress the current temp buffer
//             //printf("compressing\n");
//             strm.avail_in=tempbufferpos;
//             strm.next_in=tempbuffer;
//
//             while (strm.avail_in)
//             {
//               if (deflate(&strm, Z_NO_FLUSH)!=Z_OK)
//               {
//                 //printf("FAILURE TO COMPRESS!\n");
//                 return -1;
//               }
//               //printf("strm.avail_out=%d\n", strm.avail_out);
//
//               if (strm.avail_out==0)
//               {
//
//
//                 //printf("Out buffer full. Reallocating\n");
//                 *output=realloc(*output, maxoutputsize*2);
//
//                 strm.next_out=(unsigned char *)&(*output)[maxoutputsize];
//                 strm.avail_out=maxoutputsize;
//                 maxoutputsize=maxoutputsize*2;
//
//
//               }
//
//             }
//             tempbufferpos=0;
//          }
//
//
//
//          psymbolinfo si=(psymbolinfo)&tempbuffer[tempbufferpos];
//          si->address=symbolTable[j].st_value;
//          si->size=symbolTable[j].st_size;
//          si->type=symbolTable[j].st_info;
//          si->namelength=namelength;
//          memcpy(&si->name, symbolname, namelength);
//
//
//          tempbufferpos+=entrysize;
//        }
//      }
//
//      free(symbolTable);
//
//    }
//
//  }
//
//  for (i=0; i<b->e_shnum; i++)
//  {
//    if (stringTable[i])
//      free(stringTable[i]);
//  }
//  free(stringTable);
//
//  free(sectionHeaders);
//
//
//  debug_log("end:\n");
//  strm.avail_in=tempbufferpos;
//  strm.next_in=tempbuffer;
//
//  while (1)
//  {
//
//    i=deflate(&strm, Z_FINISH);
//    debug_log("i=%d\n", i);
//    if (i==Z_STREAM_END) //done
//      break;
//
//    if (i!=Z_OK)
//    {
//      debug_log("Failure to compress: %i\n", i);
//      return -1;
//    }
//
//    if (strm.avail_out==0)
//    {
//      debug_log("Out buffer full. Reallocating :%d\n", maxoutputsize*2);
//      *output=realloc(*output, maxoutputsize*2);
//
//      strm.next_out=(unsigned char *)&(*output)[maxoutputsize];
//      strm.avail_out=maxoutputsize;
//      maxoutputsize=maxoutputsize*2;
//
//    }
//    else
//      break;
//
//  };
//
//  /*printf("strm.avail_out=%d\n", strm.avail_out);
//
//  debug_log("total_in = %lu\n", strm.total_in);
//  debug_log("total_out = %lu\n", strm.total_out);*/
//
//  deflateEnd(&strm);
//
//
//  //update the size
//  *(uint32_t *)(&(*output)[4])=strm.total_out+3*sizeof(uint32_t);
//  *(uint32_t *)(&(*output)[8])=strm.total_in;
//
//  free(tempbuffer);
//
//  return 0;
//}
//
//int ELF64(int f, Elf64_Ehdr *b, unsigned char **output)
///*
//Caller must free output manually
//*/
//{
//  int i,j;
//
//
//  unsigned char *tempbuffer=NULL;
//  int tempbufferpos=0;
//  int maxoutputsize=TEMPBUFSIZE;
//  tempbuffer=malloc(TEMPBUFSIZE);
//
//  //setup zlib
//  z_stream strm;
//  strm.zalloc = Z_NULL;
//  strm.zfree = Z_NULL;
//  strm.opaque = Z_NULL;
//  deflateInit(&strm, 9);
//
//  *output=malloc(maxoutputsize); //allocate 256KB. This "should" be enough, but reallocate if more is needed
//
//  strm.avail_out=maxoutputsize-3*sizeof(uint32_t); //the first 8 bytes will contain the compressed and uncompressed size
//  strm.next_out=(unsigned char *)&(*output)[sizeof(uint32_t)*3];
//
//  *(uint32_t *)(&(*output)[0])=(b->e_type==ET_EXEC);
///*
//
//  debug_log("e_shoff=%lx\n", b->e_shoff);
//  debug_log("e_shentsize=%d\n", b->e_shentsize);
//  debug_log("e_shnum=%d\n", b->e_shnum);
//  debug_log("e_shstrndx=%d\n", b->e_shstrndx);*/
//
//  Elf64_Shdr *sectionHeaders=malloc(b->e_shentsize*b->e_shnum);
//
//  if (pread(f, sectionHeaders, b->e_shentsize*b->e_shnum, b->e_shoff)==-1)
//  {
//    //printf("Failure to read sectionHeaders\n");
//    deflateEnd(&strm);
//    if (sectionHeaders)
//      free(sectionHeaders);
//
//    if (output)
//      free(output);
//
//    if (tempbuffer)
//      free(tempbuffer);
//
//    return -1;
//  }
//
//  unsigned char **stringTable=calloc(b->e_shnum, sizeof(unsigned char*) );
//
//  loadStringTable64(f, sectionHeaders, stringTable, b->e_shstrndx);
//
//
//  for (i=0; i<b->e_shnum; i++)
//  {
//   // debug_log("Section %d (%lx): name=%s\n", i, sectionHeaders[i].sh_addr, &stringTable[b->e_shstrndx][sectionHeaders[i].sh_name]);
//
//    if ((sectionHeaders[i].sh_type==SHT_SYMTAB) || (sectionHeaders[i].sh_type==SHT_DYNSYM))
//    {/*
//      debug_log("Symbol data:\n", i);
//
//      debug_log("sh_addr=%lx\n", sectionHeaders[i].sh_addr);
//      debug_log("sh_offset=%lx\n", sectionHeaders[i].sh_offset);
//      debug_log("sh_size=%ld\n", sectionHeaders[i].sh_size);
//      debug_log("sh_link=%d (string table)\n", sectionHeaders[i].sh_link);
//      debug_log("sh_info=%d\n", sectionHeaders[i].sh_info);*/
//
//      Elf64_Sym *symbolTable=malloc(sectionHeaders[i].sh_size);
//      if (pread(f, symbolTable, sectionHeaders[i].sh_size, sectionHeaders[i].sh_offset)==-1)
//      {
//        //printf("Failure reading symbol table\n");
//        return -1;
//      }
//      int maxindex=sectionHeaders[i].sh_size / sizeof(Elf64_Sym);
//
//      loadStringTable64(f, sectionHeaders, stringTable, sectionHeaders[i].sh_link);
//
//      //printf("maxindex=%d\n", maxindex);
//      for (j=0; j<maxindex; j++)
//      {
//        /*
//        debug_log("symbolTable[%d]:\n", i);
//        debug_log("st_name=%s\n", &stringTable[sectionHeaders[i].sh_link][symbolTable[j].st_name] );
//        debug_log("st_value=%lx\n", symbolTable[j].st_value);
//        debug_log("st_size=%ld\n", symbolTable[j].st_size);
//        debug_log("st_info=%d\n", symbolTable[j].st_info);
//        debug_log("  Bind=%d\n", ELF64_ST_BIND(symbolTable[j].st_info));
//        debug_log("  Type=%d\n", ELF64_ST_TYPE(symbolTable[j].st_info));
//        debug_log("st_other=%d\n", symbolTable[j].st_other);
//        debug_log("st_shndx=%d\n", symbolTable[j].st_shndx);*/
//
//        if (symbolTable[j].st_value)
//        {
//          //add it to the tempbuffer
//          char *symbolname=(char *)&stringTable[sectionHeaders[i].sh_link][symbolTable[j].st_name];
//          size_t namelength=strlen(symbolname);
//          int entrysize=sizeof(symbolinfo)+namelength;
//          if (tempbufferpos+entrysize>=TEMPBUFSIZE)
//          {
//             //compress the current temp buffer
//             //printf("compressing\n");
//             strm.avail_in=tempbufferpos;
//             strm.next_in=tempbuffer;
//
//             while (strm.avail_in)
//             {
//               if (deflate(&strm, Z_NO_FLUSH)!=Z_OK)
//               {
//                 debug_log("FAILURE TO COMPRESS!\n");
//                 return -1;
//               }
//               //printf("strm.avail_out=%d\n", strm.avail_out);
//
//               if (strm.avail_out==0)
//               {
//
//                  // debug_log("Out buffer full. Reallocating\n");
//                 *output=realloc(*output, maxoutputsize*2);
//
//                 strm.next_out=(unsigned char *)&(*output)[maxoutputsize];
//                 strm.avail_out=maxoutputsize;
//                 maxoutputsize=maxoutputsize*2;
//               }
//
//             }
//             tempbufferpos=0;
//          }
//
//
//
//          psymbolinfo si=(psymbolinfo)&tempbuffer[tempbufferpos];
//          si->address=symbolTable[j].st_value;
//          si->size=symbolTable[j].st_size;
//          si->type=symbolTable[j].st_info;
//          si->namelength=namelength;
//          memcpy(&si->name, symbolname, namelength);
//
//
//          tempbufferpos+=entrysize;
//        }
//      }
//
//      free(symbolTable);
//
//    }
//
//  }
//
//  for (i=0; i<b->e_shnum; i++)
//  {
//    if (stringTable[i])
//      free(stringTable[i]);
//  }
//  free(stringTable);
//
//  free(sectionHeaders);
//
//
//  debug_log("end:\n");
//  strm.avail_in=tempbufferpos;
//  strm.next_in=tempbuffer;
//
//  while (1)
//  {
//
//    i=deflate(&strm, Z_FINISH);
//    debug_log("i=%d\n", i);
//    if (i==Z_STREAM_END) //done
//      break;
//
//    if (i!=Z_OK)
//    {
//      debug_log("Failure to compress: %i\n", i);
//      return -1;
//    }
//
//    if (strm.avail_out==0)
//    {
//      debug_log("Out buffer full. Reallocating :%d\n", maxoutputsize*2);
//      *output=realloc(*output, maxoutputsize*2);
//
//      strm.next_out=(unsigned char *)&(*output)[maxoutputsize];
//      strm.avail_out=maxoutputsize;
//      maxoutputsize=maxoutputsize*2;
//    }
//    else
//      break;
//
//  };
//
//  debug_log("strm.avail_out=%d\n", strm.avail_out);
//
//  debug_log("total_in = %lu\n", strm.total_in);
//  debug_log("total_out = %lu\n", strm.total_out);
//
//  deflateEnd(&strm);
//
//
//  *(uint32_t *)(&(*output)[4])=strm.total_out+3*sizeof(uint32_t);
//  *(uint32_t *)(&(*output)[8])=strm.total_in;
//
//
//  free(tempbuffer);
//
//  return 0; //still alive
//
//}
//
//int GetSymbolListFromFile(char *filename, unsigned char **output, int *outputsize)
///*
// * Returns a pointer to a compressed stream. The caller needs to free it
// */
//{
//  int i, f;
//  unsigned char *b=NULL;
//
//  debug_log("GetSymbolListFromFile(%s)\n", filename);
//
//  *output=NULL;
//  f=open(filename, O_RDONLY);
//  if (f==-1)
//    return -1;
//
//  b=malloc(sizeof(Elf64_Ehdr));
//  if (b)
//  {
//    i=pread(f, b, sizeof(Elf64_Ehdr), 0);
//
//    if (*(uint32_t *)b!=0x464c457f)
//      return -1; //not an ELF file
//
//    if (b[EI_CLASS]==ELFCLASS32)
//      i=ELF32(f, (Elf32_Ehdr *)b, output);
//    else
//      i=ELF64(f, (Elf64_Ehdr *)b, output);
//
//    free(b);
//  }
//
//  close(f);
//
//  return i;
//}
//
//
//int GetModuleSize32(int f, Elf32_Ehdr *b)
//{
// /* debug_log("32 bit\n");
//  debug_log("b->e_ehsize=%d  (%d)\n", (int)b->e_ehsize, (int)sizeof(Elf32_Ehdr));*/
//
//  //Elf32_Shdr *sectionHeaders=malloc(b->e_shentsize*b->e_shnum);
//  Elf32_Phdr *programHeaders=malloc(b->e_phentsize*b->e_phnum);
///*  debug_log("e_shoff=%x\n", b->e_shoff);
//  debug_log("e_shentsize=%d\n", b->e_shentsize);
//  debug_log("e_shnum=%d\n", b->e_shnum);
//  debug_log("e_shstrndx=%d\n", b->e_shstrndx);
//
//  debug_log("e_phoff=%x\n", b->e_phoff);
//  debug_log("e_phentsize=%d\n", b->e_phentsize);
//  debug_log("e_phnum=%d\n", b->e_phnum); */
//
//  if (pread(f, programHeaders, b->e_phentsize*b->e_phnum, b->e_phoff)==-1)
//  {
//    if (programHeaders)
//      free(programHeaders);
//
//    return 0;
//  }
//
//
//  int i;
//  unsigned long long lowest=0;//programHeaders[0].p_vaddr;
//  unsigned long long highest=0;//programHeaders[0].p_vaddr+programHeaders[0].p_memsz;
//
//  for (i=0; i<b->e_phnum; i++)
//  {
//     if (programHeaders[i].p_memsz>0)
//     {
//       if ((i==0) || (programHeaders[i].p_vaddr<lowest))
//          lowest=programHeaders[i].p_vaddr;
//
//       if ((i==0) || (programHeaders[i].p_vaddr+programHeaders[i].p_memsz>highest))
//          highest=programHeaders[i].p_vaddr+programHeaders[i].p_memsz;
//
//
///*
//       debug_log("%d: %x\n", i, programHeaders[i].p_type);
//       debug_log("Virtual Address: %llx-%llx:\n", (long long unsigned int)programHeaders[i].p_vaddr,(long long unsigned int)programHeaders[i].p_vaddr+(long long unsigned int)programHeaders[i].p_memsz);
//       debug_log("Size: %d (%x)\n", (int)programHeaders[i].p_memsz, (int)programHeaders[i].p_memsz);
//       */
//     }
//  }
//
//    if (programHeaders)
//      free(programHeaders);
//
// // debug_log("lowest=%llx highest=%llx\n", lowest, highest);
// // debug_log("size=%llx\n", highest-lowest);
//   return highest-lowest;
//}
//
//int GetModuleSize64(int f, Elf64_Ehdr *b)
//{
//  /*printf("64 bit\n");
//  debug_log("b->e_ehsize=%d  (%d)\n", (int)b->e_ehsize, (int)sizeof(Elf32_Ehdr));*/
//
//  Elf64_Phdr *programHeaders=malloc(b->e_phentsize*b->e_phnum);
///*  debug_log("e_shoff=%x\n", (int)b->e_shoff);
//  debug_log("e_shentsize=%d\n", b->e_shentsize);
//  debug_log("e_shnum=%d\n", b->e_shnum);
//  debug_log("e_shstrndx=%d\n", b->e_shstrndx);
//
//  debug_log("e_phoff=%x\n", (int)b->e_phoff);
//  debug_log("e_phentsize=%d\n", b->e_phentsize);
//  debug_log("e_phnum=%d\n", b->e_phnum);
//  */
//
//  if (pread(f, programHeaders, b->e_phentsize*b->e_phnum, b->e_phoff)==-1)
//  {
//    if (programHeaders)
//      free(programHeaders);
//
//    return 0;
//  }
//
//
//  int i;
//  unsigned long long lowest=0;//programHeaders[0].p_vaddr;
//  unsigned long long highest=0;//programHeaders[0].p_vaddr+programHeaders[0].p_memsz;
//
//  for (i=0; i<b->e_phnum; i++)
//  {
//     if (programHeaders[i].p_memsz>0)
//     {
//       if ((i==0) || (programHeaders[i].p_vaddr<lowest))
//          lowest=programHeaders[i].p_vaddr;
//
//       if ((i==0) || (programHeaders[i].p_vaddr+programHeaders[i].p_memsz>highest))
//          highest=programHeaders[i].p_vaddr+programHeaders[i].p_memsz;
//
//
///*
//       debug_log("%d: %x\n", i, programHeaders[i].p_type);
//       debug_log("Virtual Address: %llx-%llx:\n", (long long unsigned int)programHeaders[i].p_vaddr,(long long unsigned int)programHeaders[i].p_vaddr+(long long unsigned int)programHeaders[i].p_memsz);
//       debug_log("Size: %d (%x)\n", (int)programHeaders[i].p_memsz, (int)programHeaders[i].p_memsz);
//       */
//     }
//  }
//
//    if (programHeaders)
//      free(programHeaders);
//
// // debug_log("lowest=%llx highest=%llx\n", lowest, highest);
// // debug_log("size=%llx\n", highest-lowest);
//   return highest-lowest;
//}
//
//
//unsigned long long GetModuleSize(char *filename, unsigned long long defaultsize)
///*
// * Returns size the module will take in memory
// */
//{
//  int i,f;
//  unsigned char *b=NULL;
//  int result=defaultsize;
//
////  debug_log("GetModuleSize(\"%s\")=",filename);
//
//  f=open(filename, O_RDONLY);
//  if (f==-1)
//  {
//    debug_log("Failed to open %s\n", filename);
//    return defaultsize;
//  }
//  else
//  {
//    b=malloc(sizeof(Elf64_Ehdr));
//    if (b)
//    {
//      i=pread(f, b, sizeof(Elf64_Ehdr), 0);
//
//      if (*(uint32_t *)b!=0x464c457f)
//      {
//        debug_log("%s is not an elf\n", filename);
//        free(b);
//        close(f);
//        return defaultsize; //not an ELF file
//      }
//
//      if (b[EI_CLASS]==ELFCLASS32)
//        i=GetModuleSize32(f, (Elf32_Ehdr *)b);
//      else
//        i=GetModuleSize64(f, (Elf64_Ehdr *)b);
//
//      free(b);
//      close(f);
//
//      //printf("%x\n",i);
//      return i;
//    }
//    else
//    {
//      close(f);
//      return defaultsize;
//    }
//
//
//
//  }
//
//
//}
